__author__ = 'Muscle'

from mui.muicomponent import MUIComponent, CommandBase, MUIStatic, clamp, _MOUSE_MSGTYPE_, _MOUSE_BUTTON_
from mui.Widgets import color
from mui.Widgets.layout import LayoutBase
from mui.Widgets.muipanel import MUIStackPanel
from mui.Widgets.muidialog import PopupBase
from mui.Widgets.muitext import MUIText, TextBase
from mui.Widgets.muibutton import CheckBase, RadioBase, MUIGroupBase
from mui.Widgets.muiimage import ImageBase

import mui.muianim as Anim
from mui.CExt.CW32Window import *
from mui.muirectangle import Rectangle
import mui.InternalUse.muiconfig as config
import mui.CExt.muiDX as muiDX
import traceback

def menuOnKey(self, msgType, key, symbol, modifiers):
    if key == 'UP' or key == 'DOWN':
        children = []
        idx = -1 if key == 'DOWN' else 0
        for c in self.children:
            if isinstance(c, MenuGroup):
                for x in c.children:
                    if x.visible and x.enabled and not x.blind:
                        children.append(x)
                        if x.focus:
                            idx = len(children)-1
            elif c.visible and c.enabled and not c.blind:
                children.append(c)
                if c.focus:
                    idx = len(children)-1
        if children:
            idx += 1 if key == 'DOWN' else -1
            idx = idx % len(children)
            children[idx].setFocus()
            return True
    return False

class MenuStack(MUIStackPanel):
    def __init__(self, parent=None, **argd):
        MUIStackPanel.__init__(self, parent, **argd)
        self.bClip = argd.get('bClip', False)
        self.autosize = argd.get('autosize', True)
        self.autofit = argd.get('autofit', True)
        self.margin = argd.get('margin', (0, 15))
        self.background = 'popupmenu\\volume_bg.png|(10,10,10,10)'
        self.expandImg = 'symbol\\tree_sub.png'
        self.upImg = 'symbol\\tree_close.png'
        self.downImg = 'symbol\\tree_open.png'
        self.expandMargin = 26, 1, 2, 2
        self.subMenuIndent = 20

        self.bind('Mouse Wheel', self.scroll)

        self.scrollSpeed = 60.0

    def verticalLayout(self):
        try:
            if (self.dead):
                return
            maxHeight = self.window.height if self.autosize else None
            super(MenuStack, self).verticalLayout(maxHeight)

        except Exception, e:
            traceback.print_exc()
            return

    def relayout(self):
        super(MenuStack, self).relayout()
        window = self.window
        if window:
            self.xy = clamp(0, self.left, window.width - self.width), clamp(0, self.top, window.height - self.height)

    def getChildren(self):
        front = []
        back = []
        for i in self._children:
            if getattr(i, 'subMenuExpand', False):
                back.append(i)
            else:
                front.append(i)
        return front + back

    children = property(getChildren)

    def scroll(self, comp, x, y, dz, buttons, modifiers):
        offset = self.scrollOffset - dz / 120.0 * self.scrollSpeed
        self.onScroll(offset)
        return True

    def on_draw(self, render):
        super(MenuStack, self).on_draw(render)
        mw, mh = self.margin
        w, h = self.size
        # draw up / down button
        sw, sh = self.size
        mw, mh = self.margin

        if self.fullSize > h:
            enableUp = float(self.scrollOffset) > 0
            enableDown = float(self.scrollOffset) < self.fullSize - h
            imgOpacity = 1.0
            if not enableUp:
                imgOpacity = 0.5
            if self.upImg:
                #if enableUp:
                render.DrawBitmap(self.upImg, (0, 0, w, mh), muiDX.Uniform, imgOpacity)
            imgOpacity =  1.0
            if not enableDown:
                imgOpacity = 0.5
            if self.downImg:
                render.DrawBitmap(selfs.downImg, (0, h - mh, w, h), muiDX.Uniform, imgOpacity)

    def drawChild(self, render):
        child_in = []
        child_out = []
        child_clipped = []

        clipRect = Rectangle(self.margin[0], self.margin[1], self.width - self.margin[0], self.height - self.margin[1])

        for c in self.children:
            cRect = Rectangle(*c.rect)
            if (not cRect.IsIntersect(clipRect)):
                child_out.append(c)
            elif (c.top < self.margin[1] or c.bottom >= self.height - self.margin[1]):
                child_clipped.append(c)
            else:
                child_in.append(c)
        #render.PushAxisAlignedClip((self.margin[0], self.margin[1], self.width - self.margin[0], self.height - self.margin[1]))
        for c in child_clipped:
            c.passDraw(render)
        #render.PopAxisAlignedClip()

        for c in child_in:
            if not getattr(c, 'subMenuExpand', False):
                c.passDraw(render)
        for c in child_in:
            if getattr(c, 'subMenuExpand', False):
                c.passDraw(render)


class MUIPopupMenu(MenuStack, PopupBase):
    """PopupMenu
    """
    def __init__(self, parent=None, **argd):
        MenuStack.__init__(self, parent, **argd)
        PopupBase.__init__(self)
        self.bindCmd('Menu Return', self.modalRet)

    def relayout(self):
        super(MUIPopupMenu, self).relayout()
        window = self.window
        if window:
            self.xy = clamp(0, self.left, window.width - self.width), clamp(0, self.top, window.height - self.height)

    def getChildren(self):
        front = []
        back = []
        for i in self._children:
            if getattr(i, 'subMenuExpand', False):
                back.append(i)
            else:
                front.append(i)

        return front + back

    children = property(getChildren)


    def close(self):
        try:
            super(MUIPopupMenu, self).close()
            self.doBreakLoop()
        except Exception, e:
            traceback.print_exc()
            return

    def __hitTest(self, x, y):
        if x < 0 or x > self.width - self.margin[0]*2 or y < 0 or y > self.height - self.margin[1]*2:
            return False
        return True

    def __clickOutside(self):
        if not self.blind:
            self.onCancel()

    def __onActivateWin(self, actType, bMinimize, hwnd):
        if actType == WA_INACTIVE:
            self.onCancel()

    def doModal(self):
        self.autoRemove( self.window.bind('Activate Wnd', self.__onActivateWin) )
        return super(MUIPopupMenu, self).doModal()

    def hitTest(self, _x, _y):
        return True

    def onKey(self, msgType, key, symbol, modifiers):
        if key == 'ALT':
            self.onCancel()
            return True
        if menuOnKey(self, msgType, key, symbol, modifiers):
            return True
        return PopupBase.onKey(self, msgType, key, symbol, modifiers)


    def passMouseMsg(self, msgType, x, y, **kwargs):
        if MUIComponent.mouseCapture is not self:
            if self.passMouseMsgToChildren(msgType, x, y, **kwargs):
                return True # mouse pass to child

        return self.handleMouseMsg(msgType, x, y, **kwargs)

    def handleMouseMsg(self, msgType, x, y, **kwargs):
        if not self.__hitTest(x, y) and msgType in [_MOUSE_MSGTYPE_.press, _MOUSE_MSGTYPE_.dblclick]:
            self.__clickOutside()
            return False

        return super(MUIPopupMenu, self).handleMouseMsg(msgType, x, y, **kwargs)


class MenuItem(MUIComponent, TextBase, CommandBase):
    """ MenuItem is base item used in popup menu
    """

    def __init__(self, parent=None, **argd):
        MUIComponent.__init__(self, parent, **argd)
        TextBase.__init__(self, **argd)
        CommandBase.__init__(self, **argd)
        # default value
        self.tabStop = False
        self.height = argd.get('height', 20)
        self.bClip = False
        self.textAlign = 'LEFT'
        self.subMenu = MenuStack(self, left = 10, top = 10, width = 10, height = 10, bgColor = (1.0, 0,0,1.0), autosize = True, autofit = True, visible = False )

        # menu item
        self.margin = 5, 5, 5, 5

        # sub menu
        self.sysMenuItem = False
        self.subMenuPos = 0, 0
        self.subMenuExpand = False


        # icon
        self.icon = ''
        self.iconMargin = 0, 3, 5, 3
        self.iconWidth = 28

        self.focusImg = 'popupmenu\\popup_item.png|(7,7,7,7)'

        self.changeEvent('text', self.onHeaderChange, postEvent = False)
        self.changeEvent('subMenuExpand', self.onSubMenuExpandChange, sync=False)
        self.bind('Mouse Enter', self.onMouseEnter, postevent=False)
        self.bind('Mouse Wheel', self.scroll)
        self.bind('Focus Change', self.onFocusChange, postevent=False)
        self.bind('Parent Change', self.onParentChange)
        
        self.autoDirty(['subMenuExpand'])


    def scroll(self, comp, x, y, dz, buttons, modifiers):
        popmenu = self.popupMenu
        if (popmenu):
            _x, _y = self.local2global(x, y)
            _x, _y = popmenu.global2local(_x, _y)
            popmenu.scroll(comp, _x, _y, dz, buttons, modifiers)


    def __getHeader(self):
        return self.text

    def __setHeader(self, header):
        self.text = header

    header = property(__getHeader, __setHeader)

    def getSubMenuChildren(self):
        front = []
        back = []
        for i in self.subMenu._children:
            if getattr(i, 'subMenuExpand', False):
                back.append(i)
            else:
                front.append(i)
        return front + back

    def __getPopupMenu(self):
       parent = self.parent
       while parent:
           if isinstance(parent, MUIPopupMenu):
               return parent
           parent = parent.parent
       return None
    popupMenu = property(__getPopupMenu)


    def onSubMenuExpandChange(self):
        if self.subMenuExpand:
            pos = self.__getSubMenuPos()
            self.subMenu.left = pos[0]
            self.subMenu.top = pos[1]
            self.subMenu.visible = True
        else:
            self.subMenu.visible = False
            self.focusChild = None

    def __getSubMenuPos(self):
        mw, mh = self.subMenu.size

        # calc the expect position
        if self.sysMenuItem:
            pos = 0, self.height
        else:
            # default sub menu is on the right side
            pm = self.popupMenu
            pos = self.width - (pm.subMenuIndent if pm else 0), -(mh-self.height)/2
            #return (pm.subMenuIndent if pm else 0) - self.subMenuSize[0], 0

            # fix position to prevent outside the window
            dw, dh = self.window.size
            l, t = self.local2global(*pos)
            if l+mw > dw:      # display sub menu on left side
                _l, _t = (pm.subMenuIndent if pm else 0) - mw, pos[1]
                l, t = self.local2global(_l, _t)
            l = max(0, l)
            if mh > dh:
                t = 0
            else:
                t = clamp(0, t, dh-mh)
            pos = self.global2local(l, t)
        return pos
    #subMenuPos = property(__getSubMenuPos)

    def _addChild(self, c, **argd):
        if not self._children:
            if isinstance(c, MUIStackPanel):
                super(MenuItem, self)._addChild(c, **argd)
            return
        else:
            self.subMenu.addChild(c, **argd)
    
    def onMouseEnter(self, comp , x, y):
        self.setFocus()
        if self.subMenu.children:
            self.subMenuExpand = self.focus

    def onFocusChange(self):
        if self.children:
            if not self.focus:
                self.subMenuExpand = False

    def onParentChange(self, comp, newparent):
        if self.parent and isinstance(self.parent, Menu):
            self.sysMenuItem = True
        else:
            self.sysMenuItem = False

    def calcMenuSize(self):
        w, h = 100, 0
        for c in self.children:
            w = max(w, c.width)
            h += c.height
        return w, h

    def onCmd(self):
        if not self.subMenu.children and self.command:
            self.invokeCmd(self.command)
            self.invokeCmd('Menu Return', self.command)

    def on_draw(self, render):
        # draw background

        super(MenuItem, self).on_draw(render)
        if (self.focus and self.focusImg):
            render.DrawBitmap(self.focusImg, (0, 0, self.width, self.height), muiDX.Fill, 0.2)
        # draw icon
        if self.icon:
            pos = self.margin[0], self.iconMargin[1], self.margin[0] + self.iconWidth-self.iconMargin[2], self.bottom-self.iconMargin[3]
            if self.icon:
                render.DrawBitmap(self.icon, pos, muiDX.Uniform, imgOpacity)
        
        # draw header
        textRect = (self.margin[0]+self.iconWidth, 0, self.width-self.margin[2], self.height-self.margin[3])
        TextBase.draw(self, render, pos = textRect[:2])

        # draw expand icon
        if self.subMenu.children:
            pm = self.popupMenu
            if pm and pm.expandImg:
                m = pm.expandMargin
                pos = self.width-m[0], m[1], m[0]-m[2] + self.width-m[0], self.height-m[3]-m[1]+ m[1]
                render.DrawBitmap(pm.expandImg, pos, muiDX.Uniform, 1.0)

    def setContent(self, data):
        self.header = data


    def onHeaderChange(self):
        size = self.calTextSize(self.header)
        # * 2 for expand icon
        self.size = size[0] + self.margin[0] + self.margin[2] + self.iconWidth * 2, \
                    size[1] + self.margin[1] + self.margin[3]
    """
    def drawChild(self, render):
        print 'drawChild:', self.subMenuExpand, self.children
        if self.subMenuExpand and self.children:
            super(MenuItem, self).drawChild(render)
    """
class Menu(MUIStackPanel):
    """Menu (not ready)
    """
    def __init__(self, parent=None, **argd):
        MUIStackPanel.__init__(self, parent, color=color.lightgray, clip=False, **argd)
        self.orientation = 'Horizontal'
        self.press = False